﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using MacomberMapSystem.Common.Database;
using MacomberMapSystem.Common.Serialization;
using MacomberMapSystem.Common.Integration;
using System.IO;
using MacomberMapSystem.Common.CIM;
using MacomberMapSystem.Common.Internals;
using System.Reflection;
using MacomberMapSystem.Common.User_Interfaces;

namespace MacomberMapSystem.Common.Processing
{
    /// <summary>
    /// This class handles the exporting of CRRs
    /// </summary>
    public class CIM_CRRExporter : CIM_Processor
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="xElem"></param>
        /// <param name="NamedParameters"></param>
        /// <param name="Repository"></param>
        public CIM_CRRExporter(XmlElement xElem, Dictionary<String, Object> NamedParameters, MM_Repository Repository)
            : base(xElem, NamedParameters, Repository)
        {
        }
        /// <summary>
        /// Provide the name of our process
        /// </summary>
        public override string ProcessName
        {
            get { return "CRR Network Map Outputs"; }
        }

        /// <summary>
        /// Initiate our export
        /// </summary>
        /// <param name="DbConn"></param>
        public override enumProcessState InitiateProcess(MM_Database_Connector DbConn)
        {
            MM_Database_Model Model = NamedParameters["Model"] as MM_Database_Model;
            this.Total = (Model.Substations.Count * 2) + 1 + DbConn.Boundaries.Count + (Model.ElementsOfType("etx:ConnectivityNodeGroup").Length) + Model.ElementsOfType("cim:Company").Length + Model.ElementsOfType("cim:ACLineSegment").Length + Data_Manager.KVLevels.Count + Model.ElementsOfType("cim:SubGeographicalRegion").Length + Model.ElementsOfType("etx:SettlementLoadZone").Length;

            using (XmlTextWriter xW = new XmlTextWriter(Path.Combine(ProcessString(TargetFolder), ProcessString(TargetFileName)), Encoding.UTF8))
            {
                xW.Formatting = Formatting.Indented;
                xW.WriteStartDocument(true);
                xW.WriteStartElement("CRR_Export");
                xW.WriteAttributeString("ExportedOn", XmlConvert.ToString(DateTime.Now, XmlDateTimeSerializationMode.Unspecified));

                //Now, export the zones
                Dictionary<String, bool> ZoneNames = new Dictionary<string, bool>();
                Dictionary<CIM_Element, int> ZoneDictionary = new Dictionary<CIM_Element, int>();
                xW.WriteStartElement("Zones");
                foreach (CIM_Element Elem in Model.ElementsOfType("cim:SubGeographicalRegion"))
                    if (!ZoneNames.ContainsKey(Elem.Name))
                    {
                        WriteElem(Elem, xW, ZoneDictionary);
                        ZoneNames.Add(Elem.Name, true);
                    }
                foreach (CIM_Element Elem in Model.ElementsOfType("etx:SettlementLoadZone"))
                    if (!ZoneNames.ContainsKey(Elem.Name))
                    {
                        WriteElem(Elem, xW, ZoneDictionary);
                        ZoneNames.Add(Elem.Name, true);
                    }
                xW.WriteEndElement();

                //Now, export the companies
                xW.WriteStartElement("Companies");
                foreach (CIM_Element Elem in Model.ElementsOfType("cim:Company"))
                    WriteElem(Elem, xW, ZoneDictionary);
                xW.WriteEndElement();

                //Now, export the KV levels
                xW.WriteStartElement("KVLevels");
                foreach (MM_KVLevel KVLevel in Data_Manager.KVLevels.Values)
                    WriteElem(KVLevel, xW);
                xW.WriteEndElement();

                //Now, export the counties
                xW.WriteStartElement("Counties");
                WriteElem(Repository.Db.StateBoundary, xW);
                foreach (MM_Boundary Boundary in Repository.Db.Boundaries.Values)
                    WriteElem(Boundary, xW);
                xW.WriteEndElement();

                //Now, export the substations
                xW.WriteStartElement("Substations");
                foreach (CIM_Element Elem in Model.ElementsOfType("cim:Substation"))
                    WriteElem(Elem, xW, ZoneDictionary);
                xW.WriteEndElement();

                //Now, export the lines.
                xW.WriteStartElement("Lines");
                foreach (CIM_Element Elem in Model.ElementsOfType("cim:ACLineSegment"))
                    WriteElem(Elem, xW, ZoneDictionary);
                xW.WriteEndElement();

                //Now, export the connectivity node group information
                xW.WriteStartElement("ConnectivityNodeGroups");
                foreach (CIM_Element ConnGroup in Model.ElementsOfType("etx:ConnectivityNodeGroup"))
                    WriteElem(ConnGroup, xW, ZoneDictionary);
                xW.WriteEndElement();
            }
            return enumProcessState.Completed;
        }



        /// <summary>
        /// Write out the element
        /// </summary>
        /// <param name="Elem"></param>
        /// <param name="xW"></param>
        /// <param name="ZoneDictionary"></param>
        private void WriteElem(CIM_Element Elem, XmlTextWriter xW, Dictionary<CIM_Element, int> ZoneDictionary)
        {
            xW.WriteStartElement(Elem.ElemType);
            xW.WriteAttributeString("ElemType", Elem.ElemType);
            UInt64 TEID;
            UInt64.TryParse(Elem.TEID, out TEID);
            xW.WriteAttributeString("TEID", TEID.ToString());
            xW.WriteAttributeString("Name", MM_Type_Converter.TitleCase(Elem.LineName));
            xW.WriteAttributeString("Permitted", "true");

            if (Elem.TypeName == "cim:SubGeographicalRegion" || Elem.TypeName == "etx:SettlementLoadZone")
            {
                int Index = ZoneDictionary.Count;
                xW.WriteAttributeString("Index", Index.ToString());
                ZoneDictionary.Add(Elem, Index);
            }
            else if (Elem.TypeName == "cim:Company")
            {
                xW.WriteAttributeString("DUNS", Elem["etx:Company.DUNS"]);
                String PhoneNumber = Elem["etx:Company.PrimaryPhone"];
                long TryParse, TryParse2;
                if (long.TryParse(PhoneNumber, out TryParse))
                    if (PhoneNumber.Length == 10)
                        xW.WriteAttributeString("TelephoneNumber", TryParse.ToString("(000) 000-0000"));
                    else
                        xW.WriteAttributeString("TelephoneNumber", TryParse.ToString("(000) 000-0000 x\\." + new string('0', PhoneNumber.Trim().Length - 10)));
                else if (PhoneNumber.IndexOf("x", StringComparison.CurrentCultureIgnoreCase) != -1 && long.TryParse(PhoneNumber.Split('x', 'X')[0].Trim(), out TryParse) && long.TryParse(PhoneNumber.Split('x', 'X')[1].Trim(), out TryParse2))
                    xW.WriteAttributeString("TelephoneNumber", TryParse.ToString("(000) 000-0000") + " x." + TryParse2.ToString());
                else if (PhoneNumber == "(999) 999-9999 x____")
                    xW.WriteAttributeString("TelephoneNumber", " -Unknown- ");
                else
                    xW.WriteAttributeString("TelephoneNumber", PhoneNumber);
            }
            else if (Elem.TypeName == "cim:Substation")
            {
                xW.WriteAttributeString("ConnectedLines", ElementsToTEIDString(Elem.ConnectedLines));
                xW.WriteAttributeString("ConnectedSubstations", ElementsToTEIDString(Elem.ConnectedStations));
                xW.WriteAttributeString("LongName", MM_Type_Converter.TitleCase(Elem.LongName));

                CIM_Element LoadZone = Elem.ParseSingle("etx:SettlementLoadZone", null);
                CIM_Element WeatherZone = Elem.ParseSingle("cim:SubGeographicalRegion", null);

                if (LoadZone != null)
                    xW.WriteAttributeString("LoadZone", ZoneDictionary[LoadZone].ToString());
                if (WeatherZone != null)
                    xW.WriteAttributeString("WeatherZone", ZoneDictionary[WeatherZone].ToString());
                MM_Substation Sub;
                if (Repository.Db.Substations.TryGetValue(Elem.rdfID, out Sub))
                {
                    xW.WriteAttributeString("Latitude", Sub.Latitude.ToString());
                    xW.WriteAttributeString("Longitude", Sub.Longitude.ToString());
                    xW.WriteAttributeString("LatLong", Sub.Longitude.ToString() + "," + Sub.Latitude.ToString());
                    xW.WriteAttributeString("County", Sub.Boundary.Name);
                }

            }
            else if (Elem.TypeName == "cim:ACLineSegment")
            {
                xW.WriteAttributeString("ConnectedStations", ElementsToTEIDString(Elem.ACSegmentStations));
                xW.WriteAttributeString("Length", Elem["cim:Conductor.length"]);
                xW.WriteAttributeString("Station1", Elem.ACSegmentStations[0].TEID);
                xW.WriteAttributeString("Station2", Elem.ACSegmentStations[1].TEID);
                MM_Substation Sub1, Sub2;
                Repository.Db.Substations.TryGetValue(Elem.ACSegmentStations[0].rdfID, out Sub1);
                Repository.Db.Substations.TryGetValue(Elem.ACSegmentStations[1].rdfID, out Sub2);
                if (Sub1 != null && Sub2 != null)
                {
                    xW.WriteAttributeString("Coordinates", String.Format("{0},{1},{2},{3}", Sub1.Latitude, Sub1.Longitude, Sub2.Latitude, Sub2.Longitude));
                    xW.WriteAttributeString("LongName", Sub1.LongName + " to " + Sub2.LongName);
                }
            }
            else if (Elem.TypeName == "etx:ConnectivityNodeGroup")
            {
                xW.WriteAttributeString("PSSE_Number", Elem["etx:ConnectivityNodeGroup.PSSEBusNumber"]);
                xW.WriteAttributeString("PSSE_Name", Elem["etx:ConnectivityNodeGroup.PSSEBusName"]);
            }

            if (Elem.VoltageLevel != null)
                if ("345 KV,138 KV,69 KV".Split(',').Contains(Elem.VoltageLevel.Name))
                    xW.WriteAttributeString("KVLevel", Elem.VoltageLevel.Name);
                else
                    xW.WriteAttributeString("KVLevel", "Other KV");

            if (Elem.Substation != null)
                xW.WriteAttributeString("Substation", Elem.Substation.TEID);

            if (Elem.Owner != null)
                xW.WriteAttributeString("Owner", Elem.Owner.TEID);
            if (Elem.Operator != null)
                xW.WriteAttributeString("Operator", Elem.Operator.TEID);

            Current++;
            xW.WriteEndElement();
            xW.Flush();
        }

        /// <summary>
        /// Report a string of TEIDs
        /// </summary>
        /// <param name="Elements"></param>
        /// <returns></returns>
        private string ElementsToTEIDString(params CIM_Element[] Elements)
        {
            StringBuilder sB = new StringBuilder();
            UInt64 TEID;
            foreach (CIM_Element Elem in Elements)
                if (Elem != null && UInt64.TryParse(Elem.TEID, out TEID))
                    sB.Append((sB.Length == 0 ? "" : ",") + TEID.ToString());
            return sB.ToString();
        }

        /// <summary>
        /// Write out a boundary
        /// </summary>
        /// <param name="Boundary"></param>
        /// <param name="xW"></param>
        private void WriteElem(MM_Boundary Boundary, XmlTextWriter xW)
        {
            Boundary.WriteXml(xW);
            xW.Flush();
        }

        /// <summary>
        /// Write out an element for a boundary or KV level
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="xW"></param>
        private void WriteElem(MM_KVLevel Voltage, XmlTextWriter xW)
        {
            xW.WriteStartElement("MM_KVLevel");
            foreach (PropertyInfo pI in typeof(MM_KVLevel).GetProperties())
                if (pI.GetValue(Voltage,null) != null)
                xW.WriteAttributeString(pI.Name, MM_Serializable.GetStringForXml(pI.GetValue(Voltage, null)));
            foreach (FieldInfo fI in typeof(MM_KVLevel).GetFields())
                if (fI.FieldType != typeof(MM_Display_Parameter) && fI.GetValue(Voltage) != null)
                    xW.WriteAttributeString(fI.Name, MM_Serializable.GetStringForXml(fI.GetValue(Voltage)));
            foreach (FieldInfo fI in typeof(MM_KVLevel).GetFields())
                if (fI.FieldType == typeof(MM_Display_Parameter))
                {
                    xW.WriteStartElement(fI.Name);
                    MM_Display_Parameter disp = (MM_Display_Parameter)fI.GetValue(Voltage);
                    foreach (FieldInfo fI2 in typeof(MM_Display_Parameter).GetFields())
                        if (fI2.GetValue(disp) != null)
                        xW.WriteAttributeString(fI2.Name, MM_Serializable.GetStringForXml(fI2.GetValue(disp)));
                    foreach (PropertyInfo pI2 in typeof(MM_Display_Parameter).GetProperties())
                        if (pI2.GetValue(disp,null) != null)
                        xW.WriteAttributeString(pI2.Name, MM_Serializable.GetStringForXml(pI2.GetValue(disp,null)));
                    xW.WriteEndElement();
                }

            xW.WriteEndElement();
            xW.Flush();
        }

    }
}